﻿using System;
using System.Collections.Generic;
using Map_Creator.MainForm.Controls.MenuStrips;
using Map_Creator.Painters;
using Map_Creator.XNA;
using Microsoft.Xna.Framework;
using System.IO;
using System.Linq;
using System.Text;
using Map;
using Map_Creator.MapInfo;
using Microsoft.Xna.Framework.Graphics;

namespace Map_Creator
{
    static class SaveLoad
    {
        public static void Save(MapStruct map, string FileName)
        {
            var layers = map.Layers;
            var tileSets = map.TileSets;

            var saveFile = File.Create(FileName);

            byte[] bytes;

            // save map name length
            bytes = BitConverter.GetBytes(Encoding.Unicode.GetByteCount(map.Name));
            saveFile.Write(bytes, 0, 4);

            // save map name
            bytes = Encoding.Unicode.GetBytes(map.Name);
            saveFile.Write(bytes, 0, bytes.Length);

            // save grid size X
            bytes = BitConverter.GetBytes(map.GridSize.X);
            saveFile.Write(bytes, 0, bytes.Length);

            // save grid size Y
            bytes = BitConverter.GetBytes(map.GridSize.Y);
            saveFile.Write(bytes, 0, bytes.Length);

            // save Tile size
            bytes = BitConverter.GetBytes(map.TileSize);
            saveFile.Write(bytes, 0, bytes.Length);

            // save Levels
            bytes = BitConverter.GetBytes(map.Levels);
            saveFile.Write(bytes, 0, bytes.Length);

            // save Sets
            bytes = BitConverter.GetBytes(map.Sets);
            saveFile.Write(bytes, 0, bytes.Length);

            foreach (var layer in layers.OrderBy(l => l.Level))
            {
                // save layer name length
                bytes = BitConverter.GetBytes(Encoding.Unicode.GetByteCount(layer.Name));
                saveFile.Write(bytes, 0, 4);

                // save layer length
                bytes = Encoding.Unicode.GetBytes(layer.Name);
                saveFile.Write(bytes, 0, bytes.Length);

                // save Level
                bytes = BitConverter.GetBytes(layer.Level);
                saveFile.Write(bytes, 0, bytes.Length);

                for (int i = 0; i < map.GridSize.X; i++)
                {
                    for (int j = 0; j < map.GridSize.Y; j++)
                    {
                        // save value
                        var tile = layer.data[i, j];
                        if (tile == null)
                        {
                            // save -1 twice
                            bytes = BitConverter.GetBytes((short)-1);
                            saveFile.Write(bytes, 0, bytes.Length);
                            bytes = BitConverter.GetBytes((short)-1);
                            saveFile.Write(bytes, 0, bytes.Length);
                        }
                        else
                        {
                            bytes = BitConverter.GetBytes(layer.data[i, j].Value);
                            saveFile.Write(bytes, 0, bytes.Length);

                            bytes = BitConverter.GetBytes(layer.data[i, j].TileSetIndex);
                            saveFile.Write(bytes, 0, bytes.Length);
                        }
                    }
                }
            }

            foreach (var tileSet in tileSets.OrderBy(s => s.Index))
            {
                // save tileSet name length
                bytes = BitConverter.GetBytes(Encoding.Unicode.GetByteCount(tileSet.Name));
                saveFile.Write(bytes, 0, 4);

                // save tileSet name
                bytes = Encoding.Unicode.GetBytes(tileSet.Name);
                saveFile.Write(bytes, 0, bytes.Length);

                // save Index
                bytes = BitConverter.GetBytes(tileSet.Index);
                saveFile.Write(bytes, 0, bytes.Length);

                // save TilesImage Width
                bytes = BitConverter.GetBytes(tileSet.TilesImage.Width);
                saveFile.Write(bytes, 0, bytes.Length);
                
                // save TilesImage Height
                bytes = BitConverter.GetBytes(tileSet.TilesImage.Height);
                saveFile.Write(bytes, 0, bytes.Length);

                var data = new Color[tileSet.TilesImage.Width*tileSet.TilesImage.Height];
                //TODO: fix
                tileSet.TilesImage.GetData(data);
                // save image data
                foreach (var color in data)
                {
                    bytes = BitConverter.GetBytes(color.PackedValue);
                    saveFile.Write(bytes, 0, bytes.Length);
                }
            }

            saveFile.Close();
        }

        public static MapInfo.Map Load(string FileName)
        {
            using (var loadFile = File.OpenRead(FileName))
            {

                var bytes = new byte[255];

                var map = new MapInfo.Map();

                map.Layers = new List<LayerStruct>();

                // load map name length
                loadFile.Read(bytes, 0, 4);
                var nameLength = BitConverter.ToInt32(bytes, 0);

                if (bytes.Length < nameLength)
                    bytes = new byte[nameLength];

                // load map name
                loadFile.Read(bytes, 0, nameLength);
                map.Name = Encoding.Unicode.GetString(bytes, 0, nameLength);

                map.Node = new ExtendedTreeNode(map.Name, NodeType.Map,map);

                map.Node.ContextMenuStrip = new MapMenuStrip();

                // load grid size X
                loadFile.Read(bytes, 0, 4);
                map.GridSize.X = BitConverter.ToInt32(bytes, 0);

                // load grid size Y
                loadFile.Read(bytes, 0, 4);
                map.GridSize.Y = BitConverter.ToInt32(bytes, 0);

                // load Tile Size
                loadFile.Read(bytes, 0, 4);
                map.TileSize = BitConverter.ToInt32(bytes, 0); 

                // load Levels
                loadFile.Read(bytes, 0, 4);
                map.Levels = BitConverter.ToInt32(bytes, 0);

                // load Sets
                loadFile.Read(bytes, 0, 4);
                map.Sets = BitConverter.ToInt32(bytes, 0);

                if (MapInfo.Map.SelectedMap == null)
                    MapInfo.Map.SelectedMap = map;

                map.Selection = new Selection(map.TileSize);

                for (int l = 0; l < map.Levels; l++)
                {
                    var layer = new Layer();

                    layer.Map = map;
                    layer.data = new TileStructGrid(map.GridSize.X, map.GridSize.Y);

                    map.Layers.Add(layer);

                    // load map name length
                    loadFile.Read(bytes, 0, 4);
                    nameLength = BitConverter.ToInt32(bytes, 0);

                    if (bytes.Length < nameLength)
                        bytes = new byte[nameLength];

                    loadFile.Read(bytes, 0, nameLength);
                    layer.Name = Encoding.Unicode.GetString(bytes, 0, nameLength);

                    layer.Node = new ExtendedTreeNode(layer.Name, NodeType.Layer, layer);

                    if (!map.Node.Nodes.ContainsKey("Layers"))
                        map.Node.Nodes.Add("Layers", "Layers");
                    map.Node.Nodes["Layers"].Nodes.Add(layer.Node);

                    // load Level
                    loadFile.Read(bytes, 0, 4);
                    layer.Level = BitConverter.ToInt32(bytes, 0);

                    for (int i = 0; i < map.GridSize.X; i++)
                    {
                        for (int j = 0; j < map.GridSize.Y; j++)
                        {
                            // load value
                            loadFile.Read(bytes, 0, 2);
                            var value = BitConverter.ToInt16(bytes, 0);
                            loadFile.Read(bytes, 0, 2);
                            var tileSetIndex = BitConverter.ToInt16(bytes, 0);
                            if (value != -1 || tileSetIndex != -1)
                            {
                                layer.data[i, j] = new Tile
                                                       {
                                                           Value = value, 
                                                           TileSetIndex = tileSetIndex
                                                       };
                            }
                        }
                    }
                }

                for (int l = 0; l < map.Sets; l++)
                {
                    // load map name length
                    loadFile.Read(bytes, 0, 4);
                    nameLength = BitConverter.ToInt32(bytes, 0);

                    if (bytes.Length < nameLength)
                        bytes = new byte[nameLength];

                    loadFile.Read(bytes, 0, nameLength);
                    var name = Encoding.Unicode.GetString(bytes, 0, nameLength);

                    // load index
                    loadFile.Read(bytes, 0, 4);
                    var index = BitConverter.ToInt32(bytes, 0);

                    // load width
                    loadFile.Read(bytes, 0, 4);
                    var width = BitConverter.ToInt32(bytes, 0);

                    // load height
                    loadFile.Read(bytes, 0, 4);
                    var height = BitConverter.ToInt32(bytes, 0);

                    var data =new Color[height*width];

                    for (int i = 0; i < height * width; i++)
                    {
                        loadFile.Read(bytes, 0, 4);
                        var color = BitConverter.ToInt32(bytes, 0);

                        data[i] = new Color((byte)(color ), (byte)(color >> 8), (byte)(color >> 16), (byte)(color >> 24));
                    }
                    
                    var service = GraphicsDeviceService.AddRef(new IntPtr(), 0, 0);
                    var image = new Texture2D(service.GraphicsDevice, width, height);
                    service.Release(false);
                    image.SetData(data);
                    
                    var size = new Point(image.Width/map.TileSize, image.Height/map.TileSize);

                    var tileSet = new TileSet(image,size,index,map,name);

                    var node = new ExtendedTreeNode(name, NodeType.TileSet, tileSet);
                    tileSet.Node = node;

                    if (!map.Node.Nodes.ContainsKey("TileSets"))
                        map.Node.Nodes.Add("TileSets", "TileSets");
                    map.Node.Nodes["TileSets"].Nodes.Add(node);

                    tileSet.TileSetMap = map;

                    map.TileSets.Add(tileSet);
                }

                if (map.Sets > 0)
                    map.CurrentTileSet = (TileSet) map.TileSets[0];

                map.CurrentLayer = map[map.Levels - 1];
                return map;
            }
        }
    }
}